/**
 * @file    ws2812.c
 * @author  Frank (pq_liu@foxmail.com)
 * @brief   WS2812 Driver
 * @version 0.1
 * @date    2021-01-16
 *
 * @copyright Copyright (c) 2021
 *
 */
/* Includes ------------------------------------------------------------------*/
#include "ws2812.h"
#include "../MC3172/MC3172.h"
/* Define --------------------------------------------------------------------*/
#define delay_ms(x)     delay_us(x*1000)
/* Variables -----------------------------------------------------------------*/
//extern DMA_HandleTypeDef hdma_spi1_tx;

ws2812_t g_ws2812 = { 0 };
/* Functions Prototypes ------------------------------------------------------*/
void WS2812_Show(void);
void rgb_to_hsv(uint16_t *H, uint16_t *S, uint16_t *V, uint8_t r, uint8_t g, uint8_t b);
void hsv_to_rgb(int h, int s, int v, uint8_t *r, uint8_t *g, uint8_t *b);

/* Functions -----------------------------------------------------------------*/
void ws2812_spi_init(){
    //外设组设置
    INTDEV_SET_CLK_RST(GPCOM3_BASE_ADDR,(INTDEV_RUN|INTDEV_IS_GROUP0|INTDEV_CLK_IS_CORECLK_DIV2));
    //具体外设设置
    GPCOM_SET_OUT_PORT(GPCOM3_BASE_ADDR,(GPCOM_P1_OUTPUT_ENABLE|GPCOM_P2_OUTPUT_ENABLE| \
     GPCOM_P1_IS_MASTER_CLK| GPCOM_P2_IS_MASTER_OUT));
    //spi模式
    GPCOM_SET_COM_MODE(GPCOM3_BASE_ADDR,(GPCOM_SPI_MASTER_MODE0|GPCOM_TX_MSB_FIRST));
    //spi速度
    GPCOM_SET_COM_SPEED(GPCOM3_BASE_ADDR,24000000,2500000);
    //spi映射
    GPCOM_SET_OVERRIDE_GPIO(GPCOM3_BASE_ADDR, ( \
    GPCOM_P1_OVERRIDE_GPIO| \
    GPCOM_P2_OVERRIDE_GPIO
    ));
}

//基准时钟
void delay_us(u32 nus)
{
    u32 ticks;
    u32 told,tnow,tcnt=0;
    GET_CORE_CNT(told);                             //读取内核定时器数值
    ticks=nus*48;                                   //目标延时节拍数=需要延时时间(us)*CORE_CLK(MHz)/4
    while(1)
    {
        GET_CORE_CNT(tnow);
        if(tnow!=told)
        {
            if(tnow<told)tcnt=0xFFFFFFFF-told+tnow; //CORE_CNT递增32位，计算已延时节拍数
            else tcnt=tnow-told;
            if(tcnt>=ticks)break;                   //延时完成
        }
    };
}


//uint32 转  uint8 RGB
void Sw28_Color2RGB(uint32_t color, uint8_t* r, uint8_t* g, uint8_t* b)
{
    *b = (uint8_t)color;
    *r = (uint8_t)(color>>8);
    *g = (uint8_t)(color>>16);
}

/**
 * @brief uint8 RGB 转 uint32
 *
 * @param red
 * @param green
 * @param blue
 * @return uint32_t
 */
uint32_t WS2812_Color(uint8_t red, uint8_t green, uint8_t blue) {
	return red << 16 | green << 8 | blue;
}

/**
 * @brief 设置某一个WS2812
 *
 * @param num
 * @param RGB
 */
void WS2812_OneSet(uint8_t num, uint32_t RGB) {
	uint8_t i;
	uint32_t TempR = 0, TempG = 0, TempB = 0;

	//MSB First
	for (i = 0; i < 8; ++i, RGB >>= 1) {
		(RGB & 0x00010000) != 0 ? (TempR |= (WS2812_HIG << (i * 3))) : (TempR |= (WS2812_LOW << (i * 3)));
		(RGB & 0x00000100) != 0 ? (TempG |= (WS2812_HIG << (i * 3))) : (TempG |= (WS2812_LOW << (i * 3)));
		(RGB & 0x00000001) != 0 ? (TempB |= (WS2812_HIG << (i * 3))) : (TempB |= (WS2812_LOW << (i * 3)));
	}

	for (i = 0; i < 3; i++) {
		g_ws2812.Col[num].RGB.R[i] = TempR >> (16 - 8 * i);
		g_ws2812.Col[num].RGB.G[i] = TempG >> (16 - 8 * i);
		g_ws2812.Col[num].RGB.B[i] = TempB >> (16 - 8 * i);
	}
}

/**
 * @brief   将数据通过DMA发送去WS2812
 *
 */
void WS2812_Show(void) {
	//HAL_SPI_Transmit_DMA(&hspi2, (uint8_t*) &g_ws2812.Col[0].Buff, 9 * WS2812_NUM);
    //发送数据
    u8 tx_data_wp=0;
    for (int i = 0;i < WS2812_NUM; i++) {

//        GPCOM_SET_OUT_PORT(GPCOM3_BASE_ADDR,(GPCOM_P1_OUTPUT_ENABLE|GPCOM_P2_OUTPUT_ENABLE| \
//         GPCOM_P1_IS_MASTER_CLK| GPCOM_P2_IS_MASTER_OUT));
        for (int x = 0; x < 9; x=x+3) {
            tx_data_wp=GPCOM_GET_TX_WP(GPCOM3_BASE_ADDR);
            GPCOM_SEND_TX_DATA(GPCOM3_BASE_ADDR,tx_data_wp+0,g_ws2812.Col[i].Buff[x]);
        GPCOM_SEND_TX_DATA(GPCOM3_BASE_ADDR,tx_data_wp+1,g_ws2812.Col[i].Buff[x+1]);
        GPCOM_SEND_TX_DATA(GPCOM3_BASE_ADDR,tx_data_wp+2,g_ws2812.Col[i].Buff[x+2]);
        tx_data_wp+=3;
        GPCOM_SEND_TX_WP(GPCOM3_BASE_ADDR,tx_data_wp);
        while(!(GPCOM_TX_FIFO_EMPTY(GPCOM3_BASE_ADDR))){};

        }

    }

}

/**
 * @brief   关闭所有的WS2812
 *
 */
void WS2812_CloseAll(void) {
	uint16_t i;

	for (i = 0; i < WS2812_NUM; ++i) {
		WS2812_OneSet(i, 0);
	}
	WS2812_Show();
}

/**
 * @brief   给所有的WS2812设定某一个颜色
 *
 * @param RGB
 */
void WS2812_SetAll(uint32_t RGB) {
	uint16_t i;

	for (i = 0; i < WS2812_NUM; ++i) {
		WS2812_OneSet(i, RGB);
	}
	WS2812_Show();
}

// Fill the dots one after the other with a color
void WS2812_ColorWipe(uint32_t c, uint16_t wait) {
	for (uint16_t i = 0; i < WS2812_NUM; i++) {
		WS2812_OneSet(i, c);
		WS2812_Show();
		delay_ms(wait);
	}
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t WS2812_Wheel(uint8_t wheelPos) {
	wheelPos = 255 - wheelPos;
	if (wheelPos < 85) {
		return WS2812_Color(255 - wheelPos * 3, 0, wheelPos * 3);
	}
	if (wheelPos < 170) {
		wheelPos -= 85;
		return WS2812_Color(0, wheelPos * 3, 255 - wheelPos * 3);
	}
	wheelPos -= 170;
	return WS2812_Color(wheelPos * 3, 255 - wheelPos * 3, 0);
}

void WS2812_SingleBreatheRainbow(uint16_t wait) {
	uint16_t i, j;

	for (j = 0; j < 256; j++) {
		for (i = 0; i < WS2812_NUM; i++) {
			WS2812_OneSet(i, WS2812_Wheel((i + j) & 255));
		}
		WS2812_Show();
		delay_ms(wait);
	}
}

// Slightly different, this makes the rainbow equally distributed throughout
void WS2812_RainbowRotate(uint16_t wait) {
	uint16_t i, j;

	for (j = 0; j < 256 * 5; j++) { // 5 cycles of all colors on wheel
		for (i = 0; i < WS2812_NUM; i++) {
			WS2812_OneSet(i, WS2812_Wheel(((i * 256 / WS2812_NUM) + j) & 255));
		}
		WS2812_Show();
		delay_ms(wait);
	}
}

//Theatre-style crawling lights.
void WS2812_TheaterChase(uint32_t c, uint16_t wait) {
	for (int j = 0; j < 10; j++)            //do 10 cycles of chasing
			{
		for (int q = 0; q < 4; q++) {
			for (uint16_t i = 0; i < WS2812_NUM; i = i + 4) {
				WS2812_OneSet(i + q, c);    //turn x pixel on
			}
			WS2812_Show();

			delay_ms(wait);

			for (uint16_t i = 0; i < WS2812_NUM; i = i + 4) {
				WS2812_OneSet(i + q, 0);    //turn x pixel off
			}
		}
	}
}

//Theatre-style crawling lights with rainbow effect
void WS2812_TheaterChaseRainbow(uint16_t wait) {
	for (int j = 0; j < 256; j++)           // cycle all 256 colors in the wheel
			{
		for (int q = 0; q < 4; q++) {
			for (uint16_t i = 0; i < WS2812_NUM; i = i + 4) {
				WS2812_OneSet(i + q, WS2812_Wheel((i + j) % 255));  //turn x pixel on
			}
			WS2812_Show();

			delay_ms(wait);

			for (uint16_t i = 0; i < WS2812_NUM; i = i + 4) {
				WS2812_OneSet(i + q, 0);                            //turn x pixel off
			}
		}
	}
}

//hsv和rgb互相转
float retmax(float a, float b, float c)                            //求最大值
{
	float max = 0;
	max = a;
	if (max < b)
		max = b;
	if (max < c)
		max = c;
	return max;
}
float retmin(float a, float b, float c)                            //求最小值
{
	float min = 0;
	min = a;
	if (min > b)
		min = b;
	if (min > c)
		min = c;
	return min;
}
//R,G,B参数传入范围（0~100）
//转换结果h(0~360),s(0~100),v(0~100)
void rgb_to_hsv(uint16_t *H, uint16_t *S, uint16_t *V, uint8_t r, uint8_t g, uint8_t b) {
	float max = 0, min = 0;
	float R = r;
	float G = g;
	float B = b;
	float h, s, v;

	R = R / 255.0;
	G = G / 255.0;
	B = B / 255.0;

	max = retmax(R, G, B);
	min = retmin(R, G, B);
	v = max;
	if (max == 0)
		s = 0;
	else
		s = 1 - (min / max);

	if (max == min)
		h = 0;
	else if (max == R && G >= B)
		h = 60 * ((G - B) / (max - min));
	else if (max == R && G < B)
		h = 60 * ((G - B) / (max - min)) + 360;
	else if (max == G)
		h = 60 * ((B - R) / (max - min)) + 120;
	else if (max == B)
		h = 60 * ((R - G) / (max - min)) + 240;

	v = v * 100;
	s = s * 100;

	*H = (int) h;
	*S = (int) s;
	*V = (int) v;
}

//参数入参范围h(0~360),s(0~100),v(0~100),这里要注意，要把s,v缩放到0~1之间
//转换结果R(0~100),G(0~100),B(0~100)，如需转换到0~255，只需把后面的乘100改成乘255
void hsv_to_rgb(int h, int s, int v, uint8_t *r, uint8_t *g, uint8_t *b) {
	float C = 0, X = 0, Y = 0, Z = 0;
	int i = 0;
	float H = (float) (h);
	float S = (float) (s) / 100.0;
	float V = (float) (v) / 100.0;
	float R, G, B;
	if (S == 0)
		R = G = B = V;
	else {
		H = H / 60;
		i = (int) H;
		C = H - i;

		X = V * (1 - S);
		Y = V * (1 - S * C);
		Z = V * (1 - S * (1 - C));
		switch (i) {
		case 0:
			R = V;
			G = Z;
			B = X;
			break;
		case 1:
			R = Y;
			G = V;
			B = X;
			break;
		case 2:
			R = X;
			G = V;
			B = Z;
			break;
		case 3:
			R = X;
			G = Y;
			B = V;
			break;
		case 4:
			R = Z;
			G = X;
			B = V;
			break;
		case 5:
			R = V;
			G = X;
			B = Y;
			break;
		}
	}
	R = R * 255;
	G = G * 255;
	B = B * 255;
	*r = (int) R;
	*g = (int) G;
	*b = (int) B;
}
